package com.sjb.singleequipment;

import cn.hutool.core.util.StrUtil;
import com.sjb.singleequipment.model.User;
import com.sjb.singleequipment.model.UserLoginRedisInfo;
import com.sjb.singleequipment.service.UserService;
import com.sjb.singleequipment.util.NetUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import java.util.concurrent.TimeUnit;

import static com.sjb.singleequipment.RedisKeyConstant.*;

/**
 * @Author:sjb
 * @CreateTime:2024-08-18
 * @Description: 判断登录是否冲突以及登录逻辑
 * @Version：1.0
 */
@Slf4j
@Component
public class SessionManager {



    @Value("${spring.session.timeout}")
    private long sessionTimeout;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Lazy
    @Resource
    private UserService userService;

    @Resource
    private RedisIndexedSessionRepository sessionRepository;

    /**
     * 设置属性
     * @param request
     * @param key
     * @param value
     * @param login
     */
    public void setAttribute(HttpServletRequest request, String key, Object value,boolean login) {
        HttpSession session = request.getSession();
        if (login){
            UserLoginRedisInfo userLoginRedisInfo = (UserLoginRedisInfo) value;
            User user = userLoginRedisInfo.getUser();
            // 存储登录态
            session.setAttribute(key,user);

            // 存储 sessionId 和ip 信息
            String sessionId = session.getId();
            String userExtraInfoKey = RedisKeyUtil.getUserExtraInfoKey(user.getId());

            stringRedisTemplate.opsForHash().put(userExtraInfoKey,SESSION_ID,sessionId);
            stringRedisTemplate.opsForHash().put(userExtraInfoKey,IP,userLoginRedisInfo.getIp());
            stringRedisTemplate.expire(userExtraInfoKey,sessionTimeout, TimeUnit.SECONDS);
        }else {
            session.setAttribute(key,value);
        }
    }

    /**
     * 设置登录属性
     * @param request
     * @param loginKey
     * @param userLoginRedisInfo
     */
    public void setLoginAttribute(HttpServletRequest request,String loginKey,UserLoginRedisInfo userLoginRedisInfo){
        setAttribute(request,loginKey,userLoginRedisInfo,true);
    }

    /**
     * 删除属性
     * @param request
     * @param key
     */
    public  void removeAttribute(HttpServletRequest request,String key) {
        HttpSession session = request.getSession();
        session.removeAttribute(key);
    }

    /**
     * 退出登录
     * @param request
     */
    public void logOut(HttpServletRequest request){
        User loginUser = userService.getLoginUser(request);
        removeAttribute(request,USER_LOGIN_STATE);
        stringRedisTemplate.delete(RedisKeyUtil.getUserExtraInfoKey(loginUser.getId()));

    }

    /**
     * 检查是否已在其他端登录
     * @param userId
     * @param currentIp
     * @return
     */
    public String checkOthLogin(Long userId,String currentIp,HttpServletRequest request){
        // 检验sessionId
        Object oldSessionIdObj = stringRedisTemplate.opsForHash().get(RedisKeyUtil.getUserExtraInfoKey(userId), SESSION_ID);
        String oldSessionId = null;
        if (oldSessionIdObj != null){
            oldSessionId = oldSessionIdObj.toString();
        }

        // 校验ip
        Object oldIpObj = stringRedisTemplate.opsForHash().get(RedisKeyUtil.getUserExtraInfoKey(userId), IP);
        String oldIp = null;
        if (oldIpObj != null){
            oldIp = (String) oldIpObj;
        }
        /**
         * 判断 sessionId 如果
         *   为空或相等返回 null
         *   不等 判断ip
         *      为空或相等返回null
         *      不等 返回oldSessionId
         */
        if (StrUtil.isBlank(oldSessionId) || oldSessionId.equals(request.getSession().getId())) {
            return null;
        }else {
            if (StrUtil.isBlank(oldIp) || oldIp.equals(currentIp)) {
                return null;
            }
        }
        return oldSessionId;
    }

    /**
     * 删除其他session的登录属性
     * @param sessionId
     * @param userId
     */
    public void removeOtherSessionLoginAttribute(String sessionId,Long userId){
        String sessionKey = RedisKeyUtil.getSessionKey(sessionId);
        String sessionAttrKey = RedisKeyUtil.getSessionAttrKey(USER_LOGIN_STATE);
        // 删除 用户的额外信息
        Boolean userExtraInfoDelete = stringRedisTemplate.delete(RedisKeyUtil.getUserExtraInfoKey(userId));
        Long delete = sessionRepository.getSessionRedisOperations().opsForHash().delete(sessionKey, sessionAttrKey);
        log.info("oldSessionId:{},user extra info delete result:{},user login state delete result:{}",
                sessionId,userExtraInfoDelete,delete);

    }

    /**
     * 登录
     * @param user
     * @param request
     * @return
     */
    public String login(User user,HttpServletRequest request){
        String msg = "登录成功";
        String ipAddress = NetUtils.getIpAddress(request);
        String oldSessionId  = this.checkOthLogin(user.getId(),ipAddress,request);
        // 不为空 说明已在其他端登录
        if (StrUtil.isNotBlank(oldSessionId)){
            // 删除 oldSessionId 的登录态
            this.removeOtherSessionLoginAttribute(oldSessionId, user.getId());
            msg += "，已移除其他设备的登录";
        }
        UserLoginRedisInfo userLoginRedisInfo = UserLoginRedisInfo.builder()
                .user(user)
                .ip(ipAddress)
                .build();
        this.setLoginAttribute(request,USER_LOGIN_STATE, userLoginRedisInfo);
        return msg;
    }
}
